package com.hzeport.bgs22emduty.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.hzeport.bgs22emduty.service.CacheService;
import lombok.extern.slf4j.Slf4j;
import org.apache.cxf.common.util.CacheMap;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Service
@ConditionalOnProperty(prefix = "system-config", name = "cacheImpl", havingValue = "guava")
@Slf4j
public class GuavaCacheServiceImpl implements CacheService {

    //过期时间为key存放cache
    private final Map<Integer, Cache<String, Object>> cacheMap = new CacheMap<>();

    //无过期时间cache
    private final Cache<String, Object> cache = CacheBuilder.newBuilder().build();

    @Override
    public <T> T getSave(String key, Supplier<T> supplier, Integer expireTime, Class<T> clazz) {
        if (key == null || supplier == null || expireTime == null || clazz == null) {
            log.info("调用getSave参数为空");
            return null;
        }
        try {
            //有过期时间
            if (expireTime > 0) {
                //存在当前过期时间的key
                if (cacheMap.containsKey(expireTime)) {
                    Object value = cacheMap.get(expireTime).getIfPresent(key);
                    if (value == null) {
                        T result = supplier.get();
                        cacheMap.get(expireTime).put(key, result);
                        log.info("未查询到key：" + key + "的缓存，将supplier存入缓存并返回");
                        return result;
                    } else {
                        log.info("查询到key：" + key + "的缓存");
                        return clazz.cast(value);
                    }
                } else {
                    //不存在当前过期时间的key则新建一个cache放入cacheMap
                    LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
                            .expireAfterAccess(expireTime, TimeUnit.SECONDS).build(new CacheLoader<>() {
                                @Override
                                public T load(String key) {
                                    log.info("未查询到key：" + key + "的缓存，将supplier存入缓存并返回");
                                    return supplier.get();
                                }
                            });
                    cacheMap.put(expireTime, cache);
                    return clazz.cast(cache.getUnchecked(key));
                }
            } else {
                //没过期时间直接存
                Object value = cache.getIfPresent(key);
                if (value == null) {
                    T result = supplier.get();
                    cache.put(key, result);
                    log.info("未查询到key：" + key + "的缓存，将supplier存入缓存并返回");
                    return result;
                } else {
                    log.info("查询到key：" + key + "的缓存");
                    return clazz.cast(value);
                }
            }
        } catch (Exception e) {
            log.error("getSave获取缓存异常：{}", e.getMessage());
            return null;
        }
    }

    @Override
    public <T> List<T> getSaveList(String key, Supplier<List<T>> supplier, Integer expireTime, Class<T> clazz) {
        if (key == null || supplier == null || expireTime == null || clazz == null) {
            log.info("调用getSaveList参数为空");
            return null;
        }
        try {
            //有过期时间
            if (expireTime > 0) {
                //存在当前过期时间的key
                if (cacheMap.containsKey(expireTime)) {
                    Object value = cacheMap.get(expireTime).getIfPresent(key);
                    if (value == null) {
                        List<T> result = supplier.get();
                        cacheMap.get(expireTime).put(key, result);
                        log.info("未查询到key：" + key + "的缓存，将supplier存入缓存并返回");
                        return result;
                    } else {
                        log.info("查询到key：" + key + "的缓存");
                        return JSONArray.parseArray(JSONObject.toJSONString(value), clazz);
                    }
                } else {
                    //不存在当前过期时间的key则新建一个cache放入cacheMap
                    LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
                            .expireAfterAccess(expireTime, TimeUnit.SECONDS).build(new CacheLoader<>() {
                                @Override
                                public List<T> load(String key) {
                                    log.info("未查询到key：" + key + "的缓存，将supplier存入缓存并返回");
                                    return supplier.get();
                                }
                            });
                    cacheMap.put(expireTime, cache);
                    return JSONArray.parseArray(JSONObject.toJSONString(cache.getUnchecked(key)), clazz);
                }
            } else {
                //没过期时间直接存
                Object value = cache.getIfPresent(key);
                if (value == null) {
                    List<T> result = supplier.get();
                    cache.put(key, result);
                    log.info("未查询到key：" + key + "的缓存，将supplier存入缓存并返回");
                    return result;
                } else {
                    log.info("查询key：" + key + "的缓存");
                    return JSONArray.parseArray(JSONObject.toJSONString(value), clazz);
                }
            }
        } catch (Exception e) {
            log.error("getSaveList获取缓存异常：{}", e.getMessage());
            return null;
        }
    }

    @Override
    public <T> T get(String key, Class<T> clazz) {
        try {
            if (key != null) {
                T value = clazz.cast(cache.getIfPresent(key));
                if (value == null) {
                    for (Map.Entry<Integer, Cache<String, Object>> entry : cacheMap.entrySet()) {
                        Cache<String, Object> v = entry.getValue();
                        Object val = v.getIfPresent(key);
                        if (val != null) {
                            log.info("查询到key：" + key + "的缓存");
                            return clazz.cast(val);
                        }
                    }
                } else {
                    log.info("查询key：" + key + "的缓存");
                    return value;
                }
            }
            return null;
        } catch (Exception e) {
            log.error("get获取缓存异常：{}", e.getMessage());
            return null;
        }
    }

    @Override
    public boolean set(String key, Object value) {
        try {
            cache.put(key, value);
            log.info("key：" + key + "存入缓存");
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean set(String key, Object value, Integer expireTime) {
        try {
            if (expireTime > 0) {
                if (cacheMap.containsKey(expireTime)) {
                    cacheMap.get(expireTime).put(key, value);
                } else {
                    //不存在当前过期时间的key则新建一个cache放入cacheMap
                    Cache<String, Object> cache = CacheBuilder.newBuilder().expireAfterAccess(expireTime, TimeUnit.SECONDS).build();
                    cache.put(key, value);
                    cacheMap.put(expireTime, cache);
                }
                log.info("key：" + key + "存入缓存");
            } else {
                return set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public void del(String key) {
        if (key != null) {
            cache.invalidate(key);
            cacheMap.forEach((k, v) -> v.invalidate(key));
            log.info("删除key：" + key);
        }
    }

    @Override
    public boolean del(List<String> keys) {
        if (keys.size() > 0) {
            keys.forEach(key -> {
                cache.invalidate(key);
                cacheMap.forEach((k, v) -> v.invalidate(key));
                log.info("删除key：" + key);
            });
            return true;
        }
        return false;
    }
}
